package org.ix.dynamicapis.configure.utils;

import org.ix.dynamicapis.configure.models.*;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.scripting.xmltags.XMLScriptBuilder;
import org.apache.ibatis.session.Configuration;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class ApiTemplateUtils {

    /**
     * 获取相对全路径
     *
     * @param version 版本
     * @param topic   主题
     * @param path    导出路径
     * @return
     */
    public static String getFullPath(String version, String topic, String path) {
        if (StringUtils.isBlank(topic) || StringUtils.isBlank(version) || StringUtils.isBlank(path)) {
            return null;
        }
        String fullPath = String.format("%s/%s/%s", version, topic, path);
        return fullPath;
    }

    /**
     * 获取mybatis sql相关配置
     *
     * @param apiModel
     * @param timestamp
     * @return
     * @throws Exception
     */
    public static ApiSqlModel getApiSqlModel(ConfigModel config, ApiModel apiModel, long timestamp) throws IOException, SAXException, ParserConfigurationException {

        String mybatisSql = apiModel.getMybatisSql();
        Document doc = parseXMLDocument(mybatisSql);
        XPathParser xPathParser = new XPathParser(doc, false);
        Node node = doc.getFirstChild();
        XNode xNode = new XNode(xPathParser, node, null);
        Configuration configuration = config.getConfiguration();
        XMLScriptBuilder xmlScriptBuilder = new XMLScriptBuilder(configuration, xNode);
        SqlSource sqlSource = xmlScriptBuilder.parseScriptNode();
        MappedStatement.Builder builder = new MappedStatement.Builder(configuration, mybatisSql, sqlSource, null);
        List<ResultMap> resultMaps = new ArrayList<>();
        List<ResultMapping> resultMappings = new ArrayList<>();
        ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, mybatisSql, Map.class, resultMappings, true);
        resultMaps.add(resultMapBuilder.build());
        MappedStatement ms = builder.resultMaps(resultMaps).build();

        Map<String, Object> defaultValues = getDefaultValues(apiModel);
        ApiSqlModel apiSqlModel = new ApiSqlModel();
        apiSqlModel.setResultType(apiModel.getResultType());
        apiSqlModel.setCacheable(apiModel.getCacheable() == 1);
        apiSqlModel.setCacheTime(apiModel.getCacheTime());
        apiSqlModel.setMs(ms);
        apiSqlModel.setDefaultValues(defaultValues);
        apiSqlModel.setTimestamp(timestamp);
        apiSqlModel.setApiModel(apiModel);

        return apiSqlModel;
    }

    static Document parseXMLDocument(String xmlString) throws ParserConfigurationException, IOException, SAXException {
        if (xmlString == null) {
            log.error("接口配置SQL为空");
            throw new IllegalArgumentException("接口配置SQL为空");
        }
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        return dbf.newDocumentBuilder().parse(new InputSource(new StringReader(xmlString)));
    }


    /**
     * 获取带默认值的参数
     *
     * @param apiModel
     * @return
     */
    static Map<String, Object> getDefaultValues(ApiModel apiModel) {
        Map<String, Object> defaultValues = new HashMap<>();
        List<ApiParamModel> apiParamModels = apiModel.getApiParams();
        if (apiParamModels != null && !apiParamModels.isEmpty()) {
            apiParamModels.forEach(apiParam -> {
                if (StringUtils.isNotBlank(apiParam.getDefaultValue())) {
                    switch (apiParam.getType().toLowerCase()) {
                        case "short":
                        case "integer":
                        case "int":
                        case "long":
                            defaultValues.put(apiParam.getName(), Long.parseLong(apiParam.getDefaultValue()));
                            break;
                        case "float":
                        case "double":
                        case "decimal":
                            defaultValues.put(apiParam.getName(), new BigDecimal(apiParam.getDefaultValue()));
                            break;
                        default:
                            defaultValues.put(apiParam.getName(), apiParam.getDefaultValue());
                            break;
                    }
                }
            });
        }
        return defaultValues;
    }
}
